import warnings
import logging.config
from logging.handlers import TimedRotatingFileHandler, RotatingFileHandler
from multiprocessing import Lock

from concurrent_log_handler import ConcurrentRotatingFileHandler
from .u_config_ini import IniConfigManager
from .u_path import PathManager


default_log_formatter = logging.Formatter('[%(asctime)s][%(levelname)s][%(filename)s][line:%(lineno)d] %(message)s',
                                          "%Y-%m-%d %H:%M:%S")
# default_log_formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s',
#                                           "%Y-%m-%d %H:%M:%S")

default_log_name = "application"


class BaseLogManager:
    _instance_lock = Lock()
    _logger = dict()

    @classmethod
    def getLog(cls, logger_name: str = 'app'):
        """
        获取logger，默认带streamHandler和concurrent的fileHandler，各种参数从配置文件中获取
        如果logger_name为空，则在logger目录下以默认名作为log文件名
        否则：在上级日志目录下建立以logger_name命名的子文件夹，并在子文件夹下建立以logger_name+.log后缀命名的日志文件
        """
        if cls._logger.get(logger_name, None):
            return cls._logger.get(logger_name, None)

        with cls._instance_lock:
            log_config = IniConfigManager.get_section_config("log_cfg")
            ch_level = eval("logging." + log_config.get("ch_level", "INFO"))
            f_level = eval("logging." + log_config.get("f_level", "INFO"))
            backup_count = int(log_config.get("backup_count", 10))
            rotating_size_mb = int(log_config.get("rotating_size_mb", 20))
            max_bytes = rotating_size_mb * 1024 * 1024
            rotating_type = int(log_config.get("rotating_type", 1))
            log_dir = log_config.get("dir", PathManager.get_config_path())
            super_dir = PathManager.dir_name(log_dir)
            if not PathManager.path_exists(super_dir):
                raise FileNotFoundError(f"{super_dir} not found!")
            PathManager.make_dir(log_dir)

            if logger_name != default_log_name:
                log_dir = PathManager.make_sub_dir(log_dir, logger_name)

            log_file = PathManager.path_join(log_dir, logger_name + ".log")
            cls._logger[logger_name] = get_basic_logger(logger_name, ch_level, need_file_handler=1, log_file=log_file,
                                                        rotating_type=rotating_type, max_bytes=max_bytes,
                                                        backup_count=backup_count,
                                                        file_level=f_level)
            return cls._logger.get(logger_name, None)


class ArgIsNotLoggerError(Exception):
    def __init__(self):
        self.msg = "参数错误，不是日志句柄，无法处理"

    def __str__(self):
        return self.msg


class ArgIsNotHandlerError(Exception):
    def __init__(self):
        self.msg = "参数错误，不是日志处理器，无法处理"

    def __str__(self):
        return self.msg


class ArgIsNotFormatterError(Exception):
    def __init__(self):
        self.msg = "参数错误，不是日志格式化器，无法处理"

    def __str__(self):
        return self.msg


class NetHandler(logging.Handler):
    def __init__(self, net_client, emit_func, close_func):
        """
        网络日志Handler，需要注册网络日志发送程序
        :param net_client: 网络客户端对象
        :param emit_func: 网络日志发送函数，函数定义格式def func_name(client, msg)
        :param close_func: 网络客户端关闭或断开连接函数
        """
        logging.Handler.__init__(self)
        self.client = net_client
        self.count = 0
        self.emit_func = emit_func
        self.close_func = close_func

    def emit(self, record):
        msg = self.format(record)
        if self.client.connected:
            self.emit_func(self.client, msg)
            self.count += 1

    def __del__(self):
        self.close_func(self.client)


def get_basic_logger(logger_name: str,
                     log_level: int = 20,
                     log_formatter=None,
                     need_stream_handler: int = 1,
                     need_file_handler: int = 0,
                     log_file: str = "",
                     rotating_type: int = 0,
                     backup_count: int = 100,
                     max_bytes: int = 1024,
                     file_level: int = 30,
                     need_net_handler: int = 0,
                     net_client = None,
                     emit_func=None,
                     close_func=None,
                     net_level: int = 20):
    custom_logger = logging.getLogger(logger_name)
    custom_logger.setLevel('DEBUG')

    if not isinstance(log_formatter, logging.Formatter):
        log_formatter = default_log_formatter

    if need_stream_handler:
        stream_handler = logging.StreamHandler()
        stream_handler.setFormatter(log_formatter)
        stream_handler.setLevel(log_level)
        custom_logger.addHandler(stream_handler)

    if need_file_handler:
        if rotating_type == 1:
            file_handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count)
        elif rotating_type == 2:
            file_handler = TimedRotatingFileHandler(log_file, when="D", interval=1, backupCount=backup_count)
        elif rotating_type == 3:
            file_handler = ConcurrentRotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count)
        else:
            warnings.warn("suggest using RotatingFileHandler")
            file_handler = logging.FileHandler(log_file)

        file_handler.setFormatter(log_formatter)
        file_handler.setLevel(file_level)
        custom_logger.addHandler(file_handler)

    if need_net_handler:
        net_handler = NetHandler(net_client=net_client, emit_func=emit_func, close_func=close_func)
        net_handler.setFormatter(log_formatter)
        net_handler.setLevel(net_level)
        custom_logger.addHandler(net_handler)

    return custom_logger


def get_net_handler(net_client=None, emit_func=None, close_func=None,
                    formatter: logging.Formatter = default_log_formatter):
    net_handler = NetHandler(net_client=net_client, emit_func=emit_func, close_func=close_func)
    net_handler.setFormatter(formatter)
    return net_handler


def get_rotating_file_handler(log_file: str, max_bytes: int, backup_count: int,
                              formatter: logging.Formatter = default_log_formatter):
    rotating_file_handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count)
    rotating_file_handler.setFormatter(formatter)
    return rotating_file_handler


def get_time_ratating_file_handler(log_file: str, time_unit: str = "D", interval: int = 1, backup_count: int = 100,
                                   formatter: logging.Formatter = default_log_formatter):
    rotating_file_handler = TimedRotatingFileHandler(log_file, when=time_unit, interval=interval,
                                                     backupCount=backup_count)
    rotating_file_handler.setFormatter(formatter)
    return rotating_file_handler


def get_concurrent_file_handler(log_file: str, max_bytes: int, backup_count: int,
                                formatter: logging.Formatter = default_log_formatter):
    _handler = ConcurrentRotatingFileHandler(log_file, maxBytes=max_bytes,
                                             backupCount=backup_count, encoding="utf-8")
    _handler.setFormatter(formatter)
    return _handler


def get_file_handler(log_file: str, formatter: logging.Formatter = default_log_formatter):
    warings.warn("suggest using get_rotating_file_handler!")
    file_handler = logging.FileHandler(log_file)
    file_handler.setFormatter(formatter)
    return file_handler


def get_stream_handler(formatter: logging.Formatter = default_log_formatter):
    stream_handler = logging.StreamHandler()
    stream_handler.setFormatter(formatter)
    return stream_handler


def set_formatter(handler: logging.Handler, formatter: logging.Formatter):
    if not isinstance(handler, logging.Handler):
        raise ArgIsNotHandlerError
    handler.setFormatter(formatter)
    return handler
